/* ******************************************************** */
/* file real.c:  contains the network evaluation and weight */
/*               update functions                           */
/*                                                          */
/* Copyright (c) 1996-98 by Donald R. Tveter                */
/*                                                          */
/* ******************************************************** */

#include "cpn.h"

extern REAL eta, eta2;
extern char outstr[];
extern char debugoutput;
extern LAYER *start, *last;

extern int pg(FILE *,char *);

void kohonen()
{
 UNIT *u, *predu, *place;
 LAYER *zlayer;
 WTNODE *w;
 register REAL sum, diff;

 zlayer = start->next;
 u = (UNIT *) zlayer->units;
 while (u != NULL)
  {
   sum = 0.0;
   w = (WTNODE *) u->wtlist;
   while (w->next != NULL)
    {
     predu = (UNIT *) w->backunit;
     diff = w->weight - predu->oj;
     sum = sum + diff * diff;
     w = w->next;
    }; /* end while w */
   u->oj = sum;
   u = u->next;
  };  /* end while u */
}

void grossberg()
{ 
 UNIT *u, *predu, *place, *places[3];
 LAYER *zlayer, *ylayer;
 WTNODE *w;
 REAL sum, scale, min, minimums[3];
 int i;
/* assume no hidden layer values will be larger than BIG */
/* and for that matter that the maximum value - minimum value */
/* is less than BIG */
#define BIG 1000000.0

/* find the three smallest values in the hidden (z) layer */
zlayer = start->next;
for (i=0;i<=2;i++)
 {
  u= (UNIT *) zlayer->units;
  min = u->oj;
  place = u;
  u = u->next;
  while (u != NULL)
   {
    if (u->oj < min)
     {
      min = u->oj;
      place = u;
     };
    u = u->next;
   };
  minimums[i] = sqrt(min);
  places[i] = place;
  place->oj = place->oj + BIG;
 }; /* end for i */
sum = 0;
for (i=0;i<=2;i++)
 {
  if (minimums[i] < 0.000001) minimums[i] = 0.000001;
  sum = sum + 1 / minimums[i];
 };
scale = 1.0 / sum;
for (i=0;i<=2;i++) minimums[i] = scale / minimums[i];
/* put activation values on the hidden (z) layer */
u = (UNIT *) zlayer->units;
while (u != NULL)
 {
  u->oj = 0.0;
  u = u->next;
 };
for (i=0;i<=2;i++) places[i]->oj = minimums[i];
/* compute the values of the output units */
ylayer = last;
u = (UNIT *) ylayer->units;
while (u != NULL)
 {
  sum = 0.0;
  w = (WTNODE *) u->wtlist;
  while (w->next != NULL)
   {
    predu = (UNIT *) w->backunit;
    sum = sum + predu->oj * w->weight;
    w = w->next;
   };
  u->oj = sum;
  u = u->next;
 };
}

void forward()
{
 kohonen();
 grossberg();
}

void klearn()
{
  REAL minimum;
  int j, k;
  UNIT *u, *predu, *zi;
  LAYER *zlayer;
  WTNODE *w;

kohonen();

/* find the closest match and put its address in zi */

#ifdef DEBUG
DBG("units before:  ");
zlayer = start->next;
u = (UNIT *) zlayer->units;
while (u != NULL)
   {
    sprintf(outstr," %f",u->oj); DBG(outstr);
    u = u->next;
   };
DBG("\n");
#endif

zlayer = start->next;
u = (UNIT *) zlayer->units;
minimum = u->oj;
zi = u;
u = u->next;
while (u != NULL)
 {
  if (u->oj < minimum)
   {
    minimum = u->oj;
    zi = u;
   };
  u = u->next;
 };
/* set all the losers to 0 and the winner to 1 */
/* to make node values correct for printout */

u = (UNIT *) zlayer->units;
while (u != NULL)
 {
  if (u == zi) u->oj = 1.0; else u->oj = 0.0;
  u = u->next;
 };

#ifdef DEBUG
DBG("units after:  ");
zlayer = start->next;
u = (UNIT *) zlayer->units;
while (u != NULL)
   {
    sprintf(outstr," %f",u->oj); DBG(outstr);
    u = u->next;
   };
DBG("\n");
#endif

/* adjust the weights leading into zi */

w = (WTNODE *) zi->wtlist;
while (w->next != NULL)
 {
  predu = (UNIT *) w->backunit;
#ifdef DEBUG
sprintf(outstr," %f",w->weight); DBG(outstr);
#endif
  w->weight = w->weight + eta2 * (predu->oj - w->weight);
#ifdef DEBUG
sprintf(outstr," %f\n",w->weight); DBG(outstr);
#endif
  w = w->next;
 };
}

void glearn()
{
 UNIT *u, *predu, *zi;
 LAYER *ylayer, *zlayer;
 WTNODE *w;
 int notfound;

zlayer = start->next;
u = zlayer->units;
notfound = 1;
zi = NULL;
while (u != NULL && notfound == 1)
 {
  if (u->oj == 1.0)
   {
    zi = u;
    notfound = 0;
   };
  u = u->next;
 };
if (zi == NULL) pg(stdout,"big bug:  best value not found\n");
ylayer = last;
u = (UNIT *) ylayer->units;
while (u != NULL)
 {
  w = (WTNODE *) u->wtlist;
  while (w->next != NULL)
   {
    predu = (UNIT *) w->backunit;
    if (predu == zi) w->weight = w->weight + eta * (u->tj - w->weight);
    w = w->next;
   };
  u = u->next;
 };
}
